<?php
/**
 *+------------------
 * Lflow
 *+------------------
 * Copyright (c) 2023~2030 gitee.com/liu_guan_qing All rights reserved.本版权不可删除，侵权必究
 *+------------------
 * Author: Mr.April(405784684@qq.com)
 *+------------------
 */
declare (strict_types=1);

namespace lflow\model;

use lflow\helper\ClassHelper;
use lflow\helper\ObjectHelper;
use lflow\helper\StringHelper;
use lflow\workflow\Execution;
use lflow\handlers\IHandler;
use lflow\WorkFlowException;
use ReflectionClass;

/**
 * 自定义元素
 *
 * @author Mr.April
 * @since  1.0
 */
class CustomModel extends WorkModel
{
    /**
     * 需要执行的class类路径
     */
    private string $clazz;

    /**
     * 需要执行的class对象的方法名称
     */
    private string $methodName;

    /**
     * 执行方法时传递的参数表达式变量名称
     */
    private string $args;

    /**
     * 执行的返回值变量
     */
    private string $var;

    /**
     * 加载模型时初始化的对象实例
     */
    private object $invokeObject;

    /**
     * @throws \think\Exception
     * @throws \ReflectionException
     */
    public function exec(Execution $execution): void
    {
        if (!isset($this->invokeObject)) {
            $this->invokeObject = ClassHelper::newInstance($this->clazz());
        }
        if (!isset($this->invokeObject) || $this->invokeObject == null) {
            throw new WorkFlowException("自定义模型[class=" . $this->clazz . "]实例化对象失败");
        }
        if ($this->invokeObject instanceof IHandler) {
            $handler = $this->invokeObject;
            $handler->handle($execution);
        } else {
            $reflect = new ReflectionClass($this->invokeObject);
            $method  = $reflect->getMethod($this->methodName);
            if (!$method || !$method->isPublic() || $method->isAbstract()) {
                throw new WorkFlowException("自定义模型[class=" . $this->clazz . "]无法找到方法名称:" . $this->methodName);
            }
            $arguments   = $this->getArgsMap($execution->getArgs(), $this->args);
            $returnValue = $method->invoke($this->invokeObject, ...$arguments);
            if (StringHelper:: isNotEmpty($this->var)) {
                ObjectHelper::putAll((object)$this->var, $returnValue);
            }
        }
        $execution->getEngine()->task()->history($execution, $this);
        $this->runOutTransition($execution);
    }

    public function getClazz(): string
    {
        return $this->clazz;
    }

    public function setClazz(string $clazz): void
    {
        $this->clazz = $clazz;
    }

    public function getMethodName(): string
    {
        return $this->methodName;
    }

    public function setMethodName(string $methodName): void
    {
        $this->methodName = $methodName;
    }

    public function getArgs(): string
    {
        return $this->args;
    }

    private function getArgsMap(string $execArgs, string $args): array
    {
        if (StringHelper:: isNotEmpty($args)) {
            return (array)ObjectHelper::putAll(json_decode($execArgs), json_decode($execArgs));
        } else {
            return json_decode($execArgs, true);
        }
    }

    public function setArgs(string $args): void
    {
        $this->args = $args;
    }

    public function getVar(): string
    {
        return $this->var;
    }

    public function setVar(string $var): void
    {
        $this->var = $var;
    }
}